package client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.jxta.discovery.DiscoveryEvent;
import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.ByteArrayMessageElement;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.exception.PeerGroupException;
import net.jxta.id.IDFactory;
import net.jxta.impl.id.UUID.ModuleClassID;
import net.jxta.impl.id.UUID.PipeID;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.OutputPipe;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.Module;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.ModuleSpecAdvertisement;
import net.jxta.protocol.PipeAdvertisement;


public class Client implements DiscoveryListener, PipeMsgListener {
	
	private static String action;
	private static String file;

    public static void main(String[] args) throws PeerGroupException, IOException {

        Logger.getLogger("net.jxta").setLevel(Level.OFF);

        
        if( args.length != 0 ){
        	action = args[0];   	
        	file = args[1];
        } else {
        	System.out.println("USAGE:  client [ACTION] [FILE]");
        }
        
        
        if( !action.equals("CREATE") && !action.equals("READ") && !action.equals("DELETE") && !action.equals("PIRAPIRO") ){
        	System.out.println("OPTIONS AVAIABLE: CREATE/READ/DELETE");
        	
        }else {
        	int port = 9000 + new Random().nextInt(100);

        	Client client;
        
        	client = new Client( port );
        	client.start(); 
        	client.fetch_advertisements();
        }
    }


    private String peer_name;
    private PeerID peer_id;
    private File conf;
    private NetworkManager manager;

    public Client(int port) {
        peer_name = "Peer " + new Random().nextInt(1000000); 

        
        peer_id = IDFactory.newPeerID(PeerGroupID.defaultNetPeerGroupID, peer_name.getBytes());
        conf = new File("." + System.getProperty("file.separator") + peer_name);

        try {
            manager = new NetworkManager(
                    NetworkManager.ConfigMode.ADHOC,
                    peer_name, conf.toURI());
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        NetworkConfigurator configurator;
        try {
            configurator = manager.getConfigurator();
            configurator.setTcpPort(port);
            configurator.setTcpEnabled(true);
            configurator.setTcpIncoming(true);
            configurator.setTcpOutgoing(true);
            configurator.setUseMulticast(true);
            configurator.setPeerID(peer_id);
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static final String subgroup_name = "P2PGroup";
    private static final String subgroup_desc = "...";
    private static final PeerGroupID subgroup_id = IDFactory.newPeerGroupID(PeerGroupID.defaultNetPeerGroupID, subgroup_name.getBytes());

    private static final String unicast_name = "SERVER:SERVER";
    private static final String service_name = "server";

    private PeerGroup subgroup;
    private PipeService pipe_service;
    private PipeID unicast_id;
    private PipeID service_id;
    private DiscoveryService discovery;
    private ModuleSpecAdvertisement mdadv;
    private String server_addr;
    private String disc_addr;
    private int done = 0;

    public void start() throws PeerGroupException, IOException {

        PeerGroup net_group = manager.startNetwork();

        ModuleImplAdvertisement mAdv = null;
        try {
            mAdv = net_group.getAllPurposePeerGroupImplAdvertisement();
        } catch (Exception ex) {
            System.err.println(ex.toString());
        }
        subgroup = net_group.newGroup(subgroup_id, mAdv, subgroup_name, subgroup_desc);

        if (Module.START_OK != subgroup.startApp(new String[0]))
            System.err.println("Cannot start child peergroup");

        unicast_id = (PipeID) IDFactory.newPipeID(subgroup.getPeerGroupID(), unicast_name.getBytes());

        pipe_service = subgroup.getPipeService();
        pipe_service.createInputPipe(get_advertisement(unicast_id, false), this);

        discovery = subgroup.getDiscoveryService();
        discovery.addDiscoveryListener(this);        
        ModuleClassID mcID = (ModuleClassID) IDFactory.newModuleClassID();

        mdadv = (ModuleSpecAdvertisement)
                AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
        mdadv.setName("NAMESERVER");
        mdadv.setVersion("Version 1.0");
        mdadv.setCreator("Grupo05");
        mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
        mdadv.setSpecURI("localhost");

        service_id = (PipeID) IDFactory.newPipeID(subgroup.getPeerGroupID(), service_name.getBytes());
        PipeAdvertisement pipeadv = get_advertisement(service_id, false);
        mdadv.setPipeAdvertisement(pipeadv);

        pipe_service.createInputPipe(pipeadv, this);
    }

    private static PipeAdvertisement get_advertisement(PipeID id, boolean is_multicast) {
        PipeAdvertisement adv = (PipeAdvertisement )AdvertisementFactory.
            newAdvertisement(PipeAdvertisement.getAdvertisementType());
        adv.setPipeID(id);
        if (is_multicast)
            adv.setType(PipeService.PropagateType); 
        else 
            adv.setType(PipeService.UnicastType); 
        adv.setName("default");
        adv.setDescription("default");
        return adv;
    }

    public void discoveryEvent(DiscoveryEvent event) {
    	
    	if(done == 0){
    		StringBuffer strb = new StringBuffer();
    		strb.append("CHECK SERVER");	
    		
    		disc_addr = "urn:jxta:" + event.getSource().toString().substring(7);
    		send_to_peer( strb.toString(), disc_addr);
    		

    		if( action.equals("CREATE")){
    			try {
					FileWriter fstream = new FileWriter(file);
					BufferedWriter buff = new BufferedWriter(fstream);
					buff.write("conteudo_" + file );
					buff.close();
					
				} catch (IOException e) {
					e.printStackTrace();
				}		
	        }
    	}
    }


    private void send_to_peer(String message, String found_peer_id) {
        PipeAdvertisement adv = get_advertisement(unicast_id, false);

        Set<PeerID> ps = new HashSet<PeerID>();
        try {
            ps.add((PeerID)IDFactory.fromURI(new URI(found_peer_id)));
        } 
        catch (URISyntaxException e) {
            e.printStackTrace();
        }

        OutputPipe sender = null;
        try {
            sender = pipe_service.createOutputPipe(adv, ps, 10000);
        } 
        catch (IOException e) {
            System.out.println("Could not connect to peer");
        }

        Message msg = new Message();
        MessageElement fromElem = null;
        MessageElement msgElem = null;
        try {
            fromElem = new ByteArrayMessageElement("From", null, peer_id.toString().getBytes("ISO-8859-1"), null);
            msgElem = new ByteArrayMessageElement("Msg", null, message.getBytes("ISO-8859-1"), null);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }


         msg.addMessageElement(fromElem);
         msg.addMessageElement(msgElem);

         if(sender != null ){
        	 try {
        		 sender.send(msg);
        	 } catch (IOException e) {
        		 System.out.println("Could not send to peer");
        	 }
         }
    }

    public void pipeMsgEvent(PipeMsgEvent event) {
        try {
            Message msg = event.getMessage();
            byte[] msgBytes = msg.getMessageElement("Msg").getBytes(true);  
            byte[] fromBytes = msg.getMessageElement("From").getBytes(true); 
            String from = new String(fromBytes);
            String message = new String(msgBytes);
            
            String[] result = message.split("\\s");
            
            if(result[0].equals("BUSY")){
            	System.out.println("File in use...");
            }
            
            if(result[0].equals("DELETE")){
            	Runtime.getRuntime().exec("rm " + result[1]);
            	if( result[1].equals(file)){
            		System.out.println("File already exists!");
            	}
            }
            
            if(result[0].equals("READ")){
            	System.out.println("REC READ REQ");
            	StringBuffer sfile = new StringBuffer();
            	sfile.append("FILE ");
            	sfile.append(readFileAsString("./" + result[1] ));
            	send_to_peer(sfile.toString(), from);
            }
            
            if(result[0].equals("ID")){
            	System.out.println("REC NODE ID");
            	StringBuffer sfile = new StringBuffer();
            	sfile.append("READ ");
            	sfile.append(file);
            	send_to_peer(sfile.toString(), result[1]);
            }
            
            if(result[0].equals("FILE")){
            	System.out.println("REC FILE");
            	StringBuffer sfile = new StringBuffer();
            	sfile.append("DONE ");
            	sfile.append(file);
            	System.out.println(result[1]);
            	send_to_peer(sfile.toString(), server_addr);
            }
            
            if(result[0].equals("NFOUND")){
            	System.out.println("File not found!");
            }
            
            if(result[0].equals("CHECKED")){
            	done = 1;
            	StringBuffer sfile = new StringBuffer();
            	sfile.append(action);
            	sfile.append(" ");
            	sfile.append(file);
            	server_addr = from;
            	System.out.println(sfile.toString());
            	send_to_peer(sfile.toString(), server_addr);
            }
                  
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void fetch_advertisements() {
      new Thread("fetch advertisements thread") {
         public void run() {
            while(true) {
                discovery.getRemoteAdvertisements(null, DiscoveryService.ADV, "Name", "NAMESERVER", 1, null);
                try {
                    sleep(10000);
                }
                catch(InterruptedException e) {} 
            }
         }
      }.start();
   }
    
    
    
    private static String readFileAsString(String filePath)
    	throws java.io.IOException{
    		StringBuffer fileData = new StringBuffer(1000);
    		BufferedReader reader = new BufferedReader(new FileReader(filePath));
    		char[] buf = new char[1024];
    		int numRead=0;
    		while((numRead=reader.read(buf)) != -1){
    			String readData = String.valueOf(buf, 0, numRead);
    			fileData.append(readData);
    			buf = new char[1024];
    		}
    	reader.close();
    	return fileData.toString();
    }

}